package global;

import iterator.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import bufmgr.PageNotReadException;
import heap.*;

/*
 * @author Mark Yates
 * Provides functionality to store index information in the database so that it can be
 * retrieved for later use. 
 */
public class IDXSchema {
	public static final String IDXSCHEMA_FILE_NAME = "index_schemas";
	
	private static final AttrType[] SCHEMA_ATTR_TYPE = new AttrType[] {
			new AttrType(AttrType.attrString), 	// relname
			new AttrType(AttrType.attrString), 	// index file name
			new AttrType(AttrType.attrInteger),	// column id
			new AttrType(AttrType.attrInteger),	// IDXType
			new AttrType(AttrType.attrInteger)	// bits per dimension
			};
	private static final short[] SCHEMA_ATTR_SIZE = new short[] {(short)30, (short)36};

	public String RelName;
	public String IDXFileName;
	public int ColumnID;
	public int IDXType;
	public int Bits;
	
	public IDXSchema(String relName, String idxFileName, int columnID, int indexType, int bits) {
		this.RelName = relName;
		this.IDXFileName = idxFileName;
		this.ColumnID = columnID;
		this.IDXType = indexType;
		this.Bits = bits;
	}
	
	public IDXSchema(String relName, int columnID, String indexType, int bits) {
		this.RelName = relName;
		this.IDXFileName = getIDXFileName(relName, columnID, indexType, bits);
		this.ColumnID = columnID;
		this.IDXType = indexType.equalsIgnoreCase("B") ? IndexType.VA_BT_Index : IndexType.VA_Index;
		this.Bits = bits;
	}
	/*
	 * Gets the file name to use for the index
	 */
	public static String getIDXFileName(String relName, int columnID, String IDXType, int bits)
	{
		String format = "%s%d%s%d";
		return String.format(format,  relName, columnID, IDXType, bits);
	}
	/*
	 * gets all va indexes associated with the relation
	 */
	public static IDXSchema[] GetIndexes(String relName)
	{
		List<IDXSchema> indexes = new ArrayList<IDXSchema>();
		// setup filter to match by index file name
		CondExpr[] expr = new CondExpr[2];
		expr[0] = new CondExpr();
		expr[0].next  = null;
		expr[0].op    = new AttrOperator(AttrOperator.aopEQ);
		expr[0].type1 = new AttrType(AttrType.attrSymbol);
		expr[0].type2 = new AttrType(AttrType.attrString);
		expr[0].operand1.symbol = new FldSpec (new RelSpec(RelSpec.outer),1);
		expr[0].operand2.string = relName;
		expr[1] = null;
		// create a file scan
		Tuple tuple;
		FileScan fs = null;
		try
		{
			fs = new FileScan(IDXSCHEMA_FILE_NAME, SCHEMA_ATTR_TYPE, SCHEMA_ATTR_SIZE, (short)5, 5, getProjList(), expr);
			// add each index associated with the relation to the list of indexes
			while ((tuple = fs.get_next()) != null)
			{
				IDXSchema s = tuple2obj(tuple);
				if ( s!= null )
				{
					indexes.add(s);
				}
			}
		}
		catch (FileScanException fse)
		{
			System.err.println("Error opening index schema1 file: " + fse.getMessage());
		}
		catch (Exception e)
		{
			System.err.println("Error getting index schema1 information: " + e.getMessage());
		}
		finally
		{
			if (fs!=null)
			{
				fs.close();
			}
		}
		return indexes.toArray(new IDXSchema[indexes.size()]);
	}
	/*
	 * get index schema info by the index file name
	 */
	public static IDXSchema GetIndex(String idxFileName)
	{
		// setup filter to match by index file name
		CondExpr[] expr = new CondExpr[2];
		expr[0] = new CondExpr();
		expr[0].next  = null;
		expr[0].op    = new AttrOperator(AttrOperator.aopEQ);
		expr[0].type1 = new AttrType(AttrType.attrSymbol);
		expr[0].type2 = new AttrType(AttrType.attrString);
		expr[0].operand1.symbol = new FldSpec (new RelSpec(RelSpec.outer),2);
		expr[0].operand2.string = idxFileName;
		expr[1] = null;
		IDXSchema index = null;
		FileScan fs = null;
		try
		{
			// create a file scan
			fs = new FileScan(IDXSCHEMA_FILE_NAME, SCHEMA_ATTR_TYPE, SCHEMA_ATTR_SIZE, (short)5, 5, getProjList(), expr);
			// get the index information, if it exists
			index = tuple2obj(fs.get_next());
		}
		catch (FileScanException fse)
		{
			System.err.println("Error opening index " + idxFileName + " file: " + fse.getMessage());
		}
		catch (Exception e)
		{
			System.err.println("Error getting index " + idxFileName + " information: " + e.getMessage());
		}
		finally
		{
			if (fs!=null)
			{
				fs.close();
			}
		}
		return index;
	}
	/*
	 * inserts the index's information into the file
	 */
	public boolean InsertIndex()
	{
		try
		{
			Heapfile hf = new Heapfile(IDXSCHEMA_FILE_NAME); // This is here to create the heapfile if it doesn't already exist
			
			if (GetIndex(this.IDXFileName)!=null) {
				// Schema info for this index already exists! Just return silently
				return true;
			}
			
			// Schema info does not exist yet. Create it!
			Tuple tuple = new Tuple();
			tuple.setHdr((short) 5, SCHEMA_ATTR_TYPE, SCHEMA_ATTR_SIZE);
			tuple.setStrFld(1, this.RelName);
			tuple.setStrFld(2, this.IDXFileName);
			tuple.setIntFld(3, this.ColumnID);
			tuple.setIntFld(4, this.IDXType);
			tuple.setIntFld(5, this.Bits);
	
			hf.insertRecord(tuple.getTupleByteArray());
			
			// now done in finalizeCommand - Changes have been made to the table. Flush the pages back to disk to ensure they live on
			//SystemDefs.JavabaseBM.flushAllPages();
			return true;
		}
		catch (Exception ex) {
			return false;
		}
	}

	/*
	 * create a projection to get the index schema info from the file
	 */
	private static FldSpec[] getProjList() {
		FldSpec[] projlist = new FldSpec[5];
		RelSpec rel = new RelSpec(RelSpec.outer); 
		projlist[0] = new FldSpec(rel, 1);
		projlist[1] = new FldSpec(rel, 2);
		projlist[2] = new FldSpec(rel, 3);
		projlist[3] = new FldSpec(rel, 4);
		projlist[4] = new FldSpec(rel, 5);
		return projlist;
	}
	/*
	 * takes a tuple from the file and converts it to an instance of IDXSchema
	 */
	private static IDXSchema tuple2obj(Tuple tuple)
	{
		if (tuple==null)
		{
			return null;
		}
		
		try {
			tuple.setHdr((short) 5, SCHEMA_ATTR_TYPE, SCHEMA_ATTR_SIZE);
			String relName = tuple.getStrFld(1);
			String fName = tuple.getStrFld(2);
			int colID = tuple.getIntFld(3);
			int idxType = tuple.getIntFld(4);
			int bits = tuple.getIntFld(5);
			return new IDXSchema(relName, fName, colID, idxType, bits);
		}
		catch (Exception ex) {
			return null;
		}
	}
}
